#include "StdAfx.h"
#include "Brain.h"

CBrain::CBrain(void)
{
}

CBrain::~CBrain(void)
{
}

//void CBrain::Think( CMsgArray *inputMsgs, CMsgArray *outputMsgs )
//{
//}

void CBrain::Inspire( void )
{
}

void CBrain::Punish( void )
{
}

CBrain* CBrain::Clone( void )
{
	return new CBrain( );
}

CBrain* CBrain::Breed( CBrain& wife )
{
	return new CBrain();
}

void CNerveArray::BuildArray( int nWidth, int nDeep, int nInputGroupWidth, int nOutputGroupWidth )
{
	int i;
	m_nWidth = nWidth;
	m_nDeep = nDeep;
	m_nInputGroupWidth = nInputGroupWidth;
	m_nOutputGroupWidth = nOutputGroupWidth;

	m_pNerveArray = new CNerve* [nWidth*nDeep];

	for( i=0; i<nWidth; i+=nInputGroupWidth+nOutputGroupWidth )
	{
		int j=0,k=0;
		for( j=i; j<i+nInputGroupWidth; j++ )
		{
			int l=0;
			m_pNerveArray[ j*nDeep + 0] = CreateNerve( NERVE_TYPE_LEFT_TO_RIGHT, NERVE_TYPE_AT_LEFT_BOUND, l, j );
			for( l=1; l<nDeep-1; l++ )
				m_pNerveArray[ j*nDeep + l] = CreateNerve( NERVE_TYPE_LEFT_TO_RIGHT, NERVE_TYPE_INSIDE, l, j );
			m_pNerveArray[ j*nDeep + nDeep-1] = CreateNerve( NERVE_TYPE_LEFT_TO_RIGHT, NERVE_TYPE_AT_RIGHT_BOUND, l, j );
		}

		for( k=j; k<j+nOutputGroupWidth; k++ )
		{
			int l=0;
			m_pNerveArray[ k*nDeep + 0] = CreateNerve( NERVE_TYPE_RIGHT_TO_LEFT, NERVE_TYPE_AT_LEFT_BOUND, l, k );
			for( l=1; l<nDeep-1; l++ )
				m_pNerveArray[ k*nDeep + l] = CreateNerve( NERVE_TYPE_RIGHT_TO_LEFT, NERVE_TYPE_INSIDE, l, k );
			m_pNerveArray[ k*nDeep + nDeep-1] = CreateNerve( NERVE_TYPE_RIGHT_TO_LEFT, NERVE_TYPE_AT_RIGHT_BOUND, l, k );
		}
	}

	for( i=0; i<nWidth*nDeep; i++ )
	{
		m_pNerveArray[i]->LO_ConnectToLeft( i==(nWidth*nDeep-1) ? m_pNerveArray[0] : m_pNerveArray[i+1] );
		m_pNerveArray[i]->RO_ConnectToRight( i==(nWidth*nDeep-1) ? m_pNerveArray[0] : m_pNerveArray[i+1] );
	}
	m_pNerveArray[nWidth*nDeep-1]->LO_ConnectToLeft( m_pNerveArray[nWidth*nDeep/2] );
	m_pNerveArray[nWidth*nDeep-2]->LO_ConnectToLeft( m_pNerveArray[nWidth*nDeep-3] );
}

void CNerveArray::OneStepProcess( int* inputSignals, int* outputSignals )
{
	for( int i=0; i<m_nInputGroupWidth; i++ )
	{
		m_pNerveArray[i*m_nDeep]->LeftInput( inputSignals[i] );
		//todo: other group?
	}

	for( int j=0; j<m_nWidth*m_nDeep; j++ )
		m_pNerveArray[j]->PrepareProcess();

	for( int j=0; j<m_nWidth*m_nDeep; j++ )
		m_pNerveArray[j]->ProcessSignal();

	for( int i=0; i<m_nOutputGroupWidth; i++ )
	{
		outputSignals[i] = m_pNerveArray[(i+m_nInputGroupWidth)*m_nDeep]->GetOutput();
		//todo: other group?
	}

	m_nRunSteps++;
}

int CNerveArray::Save( unsigned char* pNerveArrayMem )
{
	unsigned char* pCurMem = pNerveArrayMem;
	*pCurMem++ = m_nWidth;
	*pCurMem++ = m_nDeep;
	*pCurMem++ = m_nInputGroupWidth;
	*pCurMem++ = m_nOutputGroupWidth;
	*((int*)pCurMem) = m_nRunSteps;
	pCurMem += sizeof(int);

	for( int i=0; i<m_nWidth*m_nDeep; i++ )
		pCurMem += m_pNerveArray[i]->Save( pCurMem );

	return pCurMem - pNerveArrayMem;
}

bool CNerveArray::Load( unsigned char* pNerveArrayMem )
{
	unsigned char* pCurMem = pNerveArrayMem;
	m_nWidth = *pCurMem++;
	m_nDeep = *pCurMem++;
	m_nInputGroupWidth = *pCurMem++;
	m_nOutputGroupWidth = *pCurMem++;
	m_nRunSteps = *((int*)pCurMem);
	pCurMem += sizeof(int);

	BuildArray( m_nWidth, m_nDeep, m_nInputGroupWidth, m_nOutputGroupWidth );

	for( int i=0; i<m_nWidth*m_nDeep; i++ )
	{
		if( !m_pNerveArray[i]->Load( pCurMem, this ) )
			return false;
		pCurMem += m_pNerveArray[i]->GetMemSize();
	}
	
	return true;
}

bool CNerve::PrepareProcess( void )
{
	m_nLeftInputSignal = m_nLeftInputSignal_Buf;
	m_nRightInputSignal = m_nRightInputSignal_Buf;
	return true;
}

bool CNerve::ProcessSignal( void )
{
	int out;

	if( m_nLeftInputSignal == m_nLeftOutputSignal )
		;
	else if( m_nLeftInputSignal > m_nLeftOutputSignal )
		m_excitability = m_excitability >= EXCITE_THRESHOLD ? m_excitability+EXCITE_SPEED_UP : m_excitability+1;
	else
		m_excitability = m_excitability <= -EXCITE_THRESHOLD ? m_excitability-EXCITE_SPEED_UP : m_excitability-1;

	if( m_excitability >= EXCITE_MAX_THRESHOLD )
		m_excitability = EXCITE_MAX_THRESHOLD;
	else if( m_excitability <= -EXCITE_MAX_THRESHOLD )
		m_excitability = -EXCITE_MAX_THRESHOLD;

	if( m_excitability >= EXCITE_THRESHOLD )
		out = (m_nLeftInputSignal + m_nRightInputSignal);
	else if( m_excitability <= -EXCITE_THRESHOLD )
		out = m_nLeftInputSignal - m_nRightInputSignal;
	else
		out = (m_nLeftInputSignal + m_nRightInputSignal)/2;

	if( m_excitability >= EXCITE_THRESHOLD || m_excitability <= -EXCITE_THRESHOLD)
	{
		out = out * m_excitability / 100 + out;
		m_calm_count++;
	}

	if( m_calm_count >= CALM_COUNT_THRESHOLD )
	{
		m_calm_count = 0;
		m_excitability = 0;
	}

	if( out < SIGNAL_MIN )
		out = SIGNAL_MIN;
	if( out > SIGNAL_MAX )
		out = SIGNAL_MAX;

	m_nLeftOutputSignal = m_nRightOutputSignal = out;

	if( m_pLeftOutputNerve )
	{
		if( m_bLO_to_LI )
			m_pLeftOutputNerve->LeftInput( m_nLeftOutputSignal );
		else
			m_pLeftOutputNerve->RightInput( m_nLeftOutputSignal );
	}

	if( m_pRightOutputNerve )
	{
		if( m_bRO_to_LI )
			m_pRightOutputNerve->LeftInput( m_nRightOutputSignal );
		else
			m_pRightOutputNerve->RightInput( m_nRightOutputSignal );
	}
	return true;;
}

int CNerve::Save( unsigned char* pNerveMem )
{
	int pos_x, pos_y;
	unsigned char* pCurNerveMem = pNerveMem;
	*pCurNerveMem++ = m_pos_x;
	*pCurNerveMem++ = m_pos_y;

	*pCurNerveMem++ = m_pLeftOutputNerve!=NULL;
	if( m_pLeftOutputNerve )
	{
		m_pLeftOutputNerve->GetPosition( pos_x, pos_y );
		*pCurNerveMem++ = pos_x;
		*pCurNerveMem++ = pos_y;
		*pCurNerveMem++ = m_bLO_to_LI;
	}
	else
	{
		*pCurNerveMem++ = 0;
		*pCurNerveMem++ = 0;
		*pCurNerveMem++ = 0;
	}

	*pCurNerveMem++ = m_pRightOutputNerve!=NULL;
	if( m_pRightOutputNerve )
	{
		m_pRightOutputNerve->GetPosition( pos_x, pos_y );
		*pCurNerveMem++ = pos_x;
		*pCurNerveMem++ = pos_y;
		*pCurNerveMem++ = m_bRO_to_LI;
	}
	else
	{
		*pCurNerveMem++ = 0;
		*pCurNerveMem++ = 0;
		*pCurNerveMem++ = 0;
	}

	*((int*)pCurNerveMem) = m_nLeftInputSignal;
	pCurNerveMem += sizeof(int);
	*((int*)pCurNerveMem) = m_nRightInputSignal;
	pCurNerveMem += sizeof(int);
	*((int*)pCurNerveMem) = m_nLeftOutputSignal;
	pCurNerveMem += sizeof(int);
	*((int*)pCurNerveMem) = m_nRightOutputSignal;
	pCurNerveMem += sizeof(int);
	*((int*)pCurNerveMem) = m_nLeftInputSignal_Buf;
	pCurNerveMem += sizeof(int);
	*((int*)pCurNerveMem) = m_nRightInputSignal_Buf;
	pCurNerveMem += sizeof(int);

	return pCurNerveMem - pNerveMem;
};

bool CNerve::Load( unsigned char* pNerveMem, CNerveArray* pArray )
{
	int pos_x, pos_y;
	unsigned char* pCurNerveMem = pNerveMem;

	pos_x = *pCurNerveMem++;
	pos_y = *pCurNerveMem++;

	if( !(m_pos_x == pos_x && m_pos_y == pos_y) )
		return false;

	if( *pCurNerveMem++ ) // m_pLeftOutputNerve!=NULL;
	{
		pos_x = *pCurNerveMem++;
		pos_y = *pCurNerveMem++;
		m_pLeftOutputNerve = pArray->GetNerve( pos_x, pos_y );
		m_bLO_to_LI = *pCurNerveMem++;

		if( m_bLO_to_LI )
			m_pLeftOutputNerve->LeftConnectIn( this );
		else
			m_pLeftOutputNerve->RightConnectIn( this );
	}
	else
		pCurNerveMem += 3;
	
	if( *pCurNerveMem++ ) // m_pRightOutputNerve!=NULL;
	{
		pos_x = *pCurNerveMem++;
		pos_y = *pCurNerveMem++;
		m_pRightOutputNerve = pArray->GetNerve( pos_x, pos_y );
		m_bRO_to_LI = *pCurNerveMem++;

		if( m_bRO_to_LI )
			m_pRightOutputNerve->LeftConnectIn( this );
		else
			m_pRightOutputNerve->RightConnectIn( this );
	}
	else
		pCurNerveMem += 3;

	m_nLeftInputSignal = *((int*)pCurNerveMem);
	pCurNerveMem += sizeof(int);
	m_nRightInputSignal = *((int*)pCurNerveMem);
	pCurNerveMem += sizeof(int);
	m_nLeftOutputSignal = *((int*)pCurNerveMem);
	pCurNerveMem += sizeof(int);
	m_nRightOutputSignal = *((int*)pCurNerveMem);
	pCurNerveMem += sizeof(int);
	m_nLeftInputSignal_Buf = *((int*)pCurNerveMem);
	pCurNerveMem += sizeof(int);
	m_nRightInputSignal_Buf = *((int*)pCurNerveMem);
	pCurNerveMem += sizeof(int);

	return true;
}

